#pragma once
#include "ParserRegexes.h"
#include "AST.h"
#include "CustomException.h"
#include "Follows.h"
#include "Parent.h"
#include "ProcTable.h"
#include "VarTable.h"
#include <string>
#include <regex>
#include <iterator>

typedef enum Symbol {
	nosymbol, integersym, namesym, lparensym, rparensym, beginsym, endsym, semicolonsym, equalsym, plussym, minussym, timessym, proceduresym, callsym, ifsym , thensym, elsesym, whilesym
};



class Parser{
private:
	std::regex universalRegex;
	std::regex_iterator<std::string::iterator> regexIterator;
	std::regex_iterator<std::string::iterator> rend;
	std::string sourceCode;
	std::string currentString;
	int stmtNumber;
	AST* ast;
	VarTable* varTable;
	ProcTable* procTable;
	Follows* follows;
	Parent* parent;

	/**
	 * set current node and its children nodeID (increasing, depth first recursive)
	 * @param node the current node
	 * @param nodeID given node ID
	 * @return the highest nodeID in the subtree whose root is the current node
	 */
	int setSubtreeNodeID(Node* node, int nodeID);
	
	/**
	 * Check which symbol a string belongs to
	 * @param str the string to be checked
	 * @return the symbol which the string belongs to
	 */
	Symbol getSymbol(std::string str);

	/**
	 * Ask for a symbol. If the string at current iterator is of this symbol, move the iterator forward, else immediately call error
	 * @param s the symbol asked
	 */
	void expect(Symbol s);

	/**
	 * Ask for variable name: NAME. If the string at current iterator is of namesym, add the variable in to varTable move the iterator forward, else immediately call error
	 * @return the node with Variable DE
	 */
	Node* expectVarName();

	/**
	 * Ask for constant value: INTEGER. If the string at current iterator is of namesym, move the iterator forward, else immediately call error
	 * @return the node with Constant DE
	 */
	Node* expectConstValue();

	/**
	 * Ask for procedure name: NAME. If the string at current iterator is of namesym, add the procedure in to procTable move the iterator forward, else immediately call error
	 * @return the node with Procedure DE
	 */
	Node* expectProcName();

	/**
	 * Ask for program: procedure+, in case of grammar violation or non-existence, call error
	 * @return the node with Program DE and its children
	 */
	Node* expectProgram();

	/**
	 * Ask for procedure: "procedure" proc_name "{"stmtList"}", in case of grammar violation or non-existence, call error
	 * @return the node with Procedure DE and its children
	 */
	Node* expectProcedure();

	/**
	 * Ask for stmt: call|while|if|assign, in case of grammar violation or non-existence, call error
	 * @return the node with DE with type corresponding to the statement type and its children
	 */
	Node* expectStmt();

	/**
	 * Ask for stmtList: stmt+, in case of grammar violation or non-existence, call error
	 * @param inheritedStmtNumber the stmtNumber to be given to the returned node
	 * @return the node with StmtList DE and its children
	 */
	Node* expectStmtList(int inheritedStmtNumber); //inheritedStmtNumber is only needed in stmtList

	/**
	 * Ask for expr: expr "+" term|expr "-" term|term, in case of grammar violation or non-existence, call error
	 * @return the node with expr DE and its children
	 */
	Node* expectExpr();

	/**
	 * Ask for term: term "*" factor|factor, in case of grammar violation or non-existence, call error
	 * @return the node with term DE and its children
	 */
	Node* expectTerm();

	/**
	 * Ask for factor: var_name|const_value|"("expr")", in case of grammar violation or non-existence, call error
	 * @return the node with term DE and its children
	 */
	Node* expectFactor();

	/**
	 * Ask for a symbol. If the string at current iterator is of this symbol, move the iterator forward
	 * @param s the symbol asked
	 * @return true of the current iterator is of the symbol, return false otherwise
	 */
	bool accept(Symbol s);

	/**
	 * Ask for variable name: NAME. If the string at current iterator is of namesym, add the variable in to varTable move the iterator forward, else return null
	 * @return the node with Variable DE or null in case of non-existence
	 */
	Node* acceptVarName();

	/**
	 * Ask for constant value: INTEGER. If the string at current iterator is of namesym, move the iterator forward, else return null
	 * @return the node with Constant DE or null in case of non-existence
	 */
	Node* acceptConstValue();

	/**
	 * Ask for procedure name: NAME. If the string at current iterator is of namesym, add the procedure in to procTable move the iterator forward, else return null
	 * @return the node with Procedure DE or null in case of non-existence
	 */
	Node* acceptProcName();

	/**
	 * Ask for procedure: "procedure" proc_name "{"stmtList"}", in case of grammar violation call error, in case of non-existence return null
	 * @return the node with Procedure DE and its children or null in case of non-existence
	 */
	Node* acceptProcedure();

	/**
	 * Ask for stmt: call|while|if|assign, in case of grammar violation call error, in case of non-existence return null
	 * @return the node with DE with type corresponding to the statement type and its children or null in case of non-existence
	 */
	Node* acceptStmt();

	void error();
	
	


public:
	/** Constructor
	 * Creates a Parser which will parse the file with the specified file name into specified AST, ProcTable, VarTable, Follows, and Parent
	 * @param fileName specified file name
	 * @param ast AST to be built
	 * @param procTable ProcTable to be built
	 * @param varTable VarTable to be built
	 * @param parent Parent to be built
	 */
	Parser(const char* fileName, AST &ast, ProcTable &procTable, VarTable &varTable, Follows &follows, Parent &parent);

	/**
	 * Build AST, procTable, varTable, follows, and parent based on the content of the file with name fileName
	 */
	void buildPKB();
};